// OpentTxl-C Version 11 TXL parse tree deconstructing functions
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// OpenTxl program parse tree deconstructors - used only in the compiler.
// Functions to deconstruct TXL language parse trees according to the TXL grammar. 
// These functions have intimate knowledge of the TXL bootstrap grammar and require updating when it is changed.

// Modification Log

// v11.0 Initial revision, adapted from OpenTxl 11.0

// v11.1 Updated to match TXL 11.1 grammar

// v11.3 Updated to match TXL 11.3 grammar
//       Added multiple search criteria

// I/O, strings, memory allocation
#include "support.h"

// Global modules
#include "limits.h"
#include "tokens.h"
#include "trees.h"
#include "idents.h"
#include "shared.h"

// Check interface consistency
#include "txltree.h"

// WARNING: These operations have intimate knowledge of the structure of TXL program parse trees
// as defined in the TXL Version 11.1 bootstrap grammar!

treePT txltree_patternOrReplacement_litsAndVarsAndExpsTP (treePT patternOrReplacementTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[patternOrReplacementTP].name], "TXL_pattern_") == 0) 
         || (stringcmp (*ident_idents[tree_trees[patternOrReplacementTP].name], "TXL_replacement_") == 0)); 
    return (tree_kids[tree_trees[patternOrReplacementTP].kidsKP]);
}

bool txltree_literalP (treePT treeTP);
tokenT txltree_literal_tokenT (treePT literalTP);

void txltree_listRepeatOrOptTargetName (treePT listRepeatOrOptTP, string targetName)
{
    if ((tree_trees[tree_kid1TP (tree_kid2TP (listRepeatOrOptTP))].kind) == treeKind_id) {
        stringcpy (targetName, (*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (listRepeatOrOptTP))].name]));
        return;
    } else {
        const tokenT literalT = txltree_literal_tokenT (tree_kid1TP (tree_kid2TP (listRepeatOrOptTP)));
        stringcpy (targetName, "lit__"), stringcat (targetName, (*ident_idents[literalT]));
        return;
    }
}

tokenT txltree_descriptionTargetT (treePT descriptionTP)
{
    // Given a list, repeat, opt or nonterminal description, 
    // return its nonterminal target name
        
    assert (stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0);
 
    string descriptionName;
    stringcpy (descriptionName, *ident_idents[tree_trees[tree_kids[tree_trees[descriptionTP].kidsKP]].name]);

    string targetName,listRepeatName;
    tokenT identIndex;

    if ((stringcmp (descriptionName, "TXL_listDescription_") == 0) || (stringcmp (descriptionName, "TXL_newlistDescription_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "list_0_"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_repeatDescription_") == 0) || (stringcmp (descriptionName, "TXL_newrepeatDescription_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "repeat_0_"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_list1Description_") == 0) || (stringcmp (descriptionName, "TXL_newlist1Description_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "list_1_"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_repeat1Description_") == 0) || (stringcmp (descriptionName, "TXL_newrepeat1Description_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "repeat_1_"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_optDescription_") == 0) || (stringcmp (descriptionName, "TXL_newoptDescription_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "opt__"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_attrDescription_") == 0) || (stringcmp (descriptionName, "TXL_newattrDescription_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "attr__"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_pushDescription_") == 0) || (stringcmp (descriptionName, "TXL_newpushDescription_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "push__"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else if ((stringcmp (descriptionName, "TXL_popDescription_") == 0) || (stringcmp (descriptionName, "TXL_newpopDescription_") == 0)) {
        txltree_listRepeatOrOptTargetName (tree_kids[tree_trees[descriptionTP].kidsKP], targetName);
        stringcpy (listRepeatName, "pop__"), stringcat (listRepeatName, targetName); 
        identIndex = ident_install (listRepeatName, treeKind_id);
    } else {
        identIndex = tree_trees[tree_kids[tree_trees[descriptionTP].kidsKP]].name;
    }

    return (identIndex);
}

tokenT txltree_construct_varNameT (treePT constructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_constructPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_importPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_exportPart_") == 0));
    return (tree_trees[tree_kid2TP (constructTP)].name);
}

tokenT txltree_construct_targetT (treePT constructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_constructPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_importPart_") == 0));
    const treePT bracketedDescriptionTP = tree_kid3TP (constructTP);
    assert ((stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0));
    const treePT descriptionTP = tree_kid2TP (bracketedDescriptionTP);
    assert ((stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0));
    return (txltree_descriptionTargetT (descriptionTP));
}

treePT txltree_construct_bracketedDescriptionTP (treePT constructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_constructPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_importPart_") == 0));
    return (tree_kid3TP (constructTP));
}

treePT txltree_construct_replacementTP (treePT constructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_constructPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_exportPart_") == 0));
    return (tree_kid4TP (constructTP));
}

bool txltree_construct_isAnonymous (treePT constructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_constructPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[constructTP].name], "TXL_exportPart_") == 0));
    const treePT replacementTP = txltree_construct_replacementTP ((treePT) constructTP);
    if (tree_plural_emptyP (tree_kid1TP (replacementTP))) {
        return (false);
    } else {
        const treePT indExpsAndLitsTP = tree_kid1TP (tree_kid1TP (replacementTP));
        if (!tree_plural_emptyP (tree_kid2TP (indExpsAndLitsTP))) {
            return (false);
        } else {
            const treePT expressionTP = tree_kid1TP (tree_kid1TP (indExpsAndLitsTP));
            if (stringcmp ((*ident_idents[tree_trees[expressionTP].name]), "TXL_expression_") != 0) {
                return (false);
            } else {
                return ((tree_trees[tree_kid1TP (expressionTP)].name) == anonymous_T);
            }
        }
    }
}

treePT txltree_construct_anonymousExpressionTP (treePT constructTP)
{
    assert (txltree_construct_isAnonymous (constructTP));
    const treePT replacementTP = txltree_construct_replacementTP (constructTP);
    const treePT indExpsAndLitsTP = tree_kid1TP (tree_kid1TP (replacementTP));
    return (tree_kid1TP (tree_kid1TP (indExpsAndLitsTP)));
}

tokenT txltree_deconstruct_varNameT (treePT deconstructTP)
{
    assert (stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0);
    return (tree_trees[tree_kid6TP (deconstructTP)].name);
}

treePT txltree_deconstruct_patternTP (treePT deconstructTP)
{
    assert (stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0);
    return (tree_kidTP (7, deconstructTP));
}

bool txltree_deconstruct_isStarred (treePT deconstructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0)
        && (stringcmp (*ident_idents[tree_trees[tree_kid4TP (deconstructTP)].name], "TXL_optStarDollarHash_") == 0));
    return ((tree_trees[tree_kid1TP (tree_kid4TP (deconstructTP))].name) == star_T);
}

bool txltree_deconstruct_negated (treePT deconstructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0)
        && (stringcmp (*ident_idents[tree_trees[tree_kid3TP (deconstructTP)].name], "TXL_optNot_") == 0));
    return (!tree_plural_emptyP (tree_kid3TP (deconstructTP)));
}

bool txltree_deconstruct_isTyped (treePT deconstructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0)
        && (stringcmp (*ident_idents[tree_trees[tree_kid5TP (deconstructTP)].name], "TXL_optBracketedDescription_") == 0));
    return (!tree_plural_emptyP (tree_kid5TP (deconstructTP)));
}

tokenT txltree_deconstruct_targetT (treePT deconstructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0)
        && (txltree_deconstruct_isTyped (deconstructTP)));
    const treePT bracketedDescriptionTP = tree_kid1TP (tree_kid5TP (deconstructTP));
    assert (stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0);
    const treePT descriptionTP = tree_kid2TP (bracketedDescriptionTP);
    assert (stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0);
    return (txltree_descriptionTargetT (descriptionTP));
}

treePT txltree_deconstruct_optSkippingTP (treePT deconstructTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[deconstructTP].name], "TXL_deconstructPart_") == 0)
        && (stringcmp (*ident_idents[tree_trees[tree_kid1TP (deconstructTP)].name], "TXL_optSkippingBracketedDescription_") == 0));
    return (tree_kid1TP (deconstructTP));
}

tokenT txltree_import_export_targetT (treePT exportTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[exportTP].name], "TXL_exportPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[exportTP].name], "TXL_importPart_") == 0));
    const treePT optBracketedDescriptionTP = tree_kid3TP (exportTP);
    assert (stringcmp (*ident_idents[tree_trees[optBracketedDescriptionTP].name], "TXL_optBracketedDescription_") == 0);
    if (!tree_plural_emptyP (optBracketedDescriptionTP)) {
        const treePT descriptionTP = tree_kid2TP (tree_kid1TP (optBracketedDescriptionTP));
        assert (stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0);
        return (txltree_descriptionTargetT (descriptionTP));
    } else {
        return (NOT_FOUND);
    }
}

treePT txltree_import_export_bracketedDescriptionTP (treePT exportTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[exportTP].name], "TXL_exportPart_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[exportTP].name], "TXL_importPart_") == 0));
    return (tree_kid1TP (tree_kid3TP (exportTP)));
}

treePT txltree_import_patternTP (treePT importTP)
{
    assert (stringcmp (*ident_idents[tree_trees[importTP].name], "TXL_importPart_") == 0);
    return (tree_kid4TP (importTP));
}

//    define TXL_ruleStatement_
//      'rule [id] [TXL_arguments_]
//          [TXL_parts_]
//          [TXL_optReplaceOrMatchPart_]
//          [TXL_parts_]
//          [TXL_optByPart_]
//      'end 'rule 
//    end define

tokenT txltree_rule_nameT (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
        ||  (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    return (tree_trees[tree_kid2TP (ruleTP)].name);
}

treePT txltree_rule_formalsTP (treePT ruleTP)
{
    assert (((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0))
       && (stringcmp (*ident_idents[tree_trees[tree_kid3TP (ruleTP)].name], "TXL_arguments_") == 0));
    return (tree_kid3TP (ruleTP));
}

treePT txltree_rule_prePatternTP (treePT ruleTP)
{
    assert (((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0))
       && (stringcmp (*ident_idents[tree_trees[tree_kid4TP (ruleTP)].name], "TXL_parts_") == 0));
    return (tree_kid4TP (ruleTP));
}

treePT txltree_rule_optReplaceOrMatchPartTP (treePT ruleTP)
{
    assert (((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0))
       && (stringcmp (*ident_idents[tree_trees[tree_kid5TP (ruleTP)].name], "TXL_optReplaceOrMatchPart_") == 0));
    return (tree_kid5TP (ruleTP));
}

treePT txltree_rule_postPatternTP (treePT ruleTP)
{
    assert (((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0))
       && (stringcmp (*ident_idents[tree_trees[tree_kid6TP (ruleTP)].name], "TXL_parts_") == 0));
    return (tree_kid6TP (ruleTP));
}

treePT txltree_rule_optByPartTP (treePT ruleTP)
{
    assert (((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0))
       && (stringcmp (*ident_idents[tree_trees[tree_kid7TP (ruleTP)].name], "TXL_optByPart_") == 0));
    return (tree_kid7TP (ruleTP));
}

// define TXL_optReplaceOrMatchPart_
//              [TXL_replaceOrMatchPart_]
//     |        [empty]
// end define
// 
// define TXL_replaceOrMatchPart_
//      [TXL_optSkippingBracketedDescription_] 
//      [TXL_replaceOrMatch_] [TXL_optStarDollarHash_]
//              [TXL_bracketedDescription_] 
//      [TXL_pattern_]
// end define

bool txltree_rule_isStarred (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    return ((tree_trees[tree_kid1TP (tree_kid3TP (replaceOrMatchPartTP))].name) == star_T);
}

bool txltree_rule_isDollared (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    return ((tree_trees[tree_kid1TP (tree_kid3TP (replaceOrMatchPartTP))].name) == dollar_T);
}

tokenT txltree_rule_replaceOrMatchT (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    return (tree_trees[tree_kid1TP (tree_kid2TP (replaceOrMatchPartTP))].name);
}

treePT txltree_rule_optSkippingTP (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    assert (stringcmp (*ident_idents[tree_trees[tree_kid1TP (replaceOrMatchPartTP)].name], 
        "TXL_optSkippingBracketedDescription_") == 0);
    return (tree_kid1TP (replaceOrMatchPartTP));
}

treePT txltree_rule_targetBracketedDescriptionTP (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    assert (stringcmp (*ident_idents[tree_trees[tree_kid4TP (replaceOrMatchPartTP)].name], "TXL_bracketedDescription_") == 0);
    return (tree_kid4TP (replaceOrMatchPartTP));
}

tokenT txltree_rule_targetT (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    const treePT bracketedDescriptionTP = tree_kid4TP (replaceOrMatchPartTP);
    assert (stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0);
    const treePT descriptionTP = tree_kid2TP ((treePT) bracketedDescriptionTP);
    assert (stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0);
    return (txltree_descriptionTargetT (descriptionTP));
}

treePT txltree_rule_patternTP (treePT ruleTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_ruleStatement_") == 0)
          || (stringcmp (*ident_idents[tree_trees[ruleTP].name], "TXL_functionStatement_") == 0));
    const treePT optReplaceOrMatchPartTP = txltree_rule_optReplaceOrMatchPartTP (ruleTP);
    assert (stringcmp (*ident_idents[tree_trees[optReplaceOrMatchPartTP].name], "TXL_optReplaceOrMatchPart_") == 0);
    assert (! tree_plural_emptyP (optReplaceOrMatchPartTP));
    const treePT replaceOrMatchPartTP = tree_kid1TP (optReplaceOrMatchPartTP);
    assert (stringcmp (*ident_idents[tree_trees[tree_kid5TP (replaceOrMatchPartTP)].name], "TXL_pattern_") == 0);
    return (tree_kid5TP (replaceOrMatchPartTP));
}

// define TXL_optByPart_
//              [TXL_byPart_]
//     |        [empty]
// end define
// 
// define TXL_byPart_
//      'by
//          [TXL_replacement_]
// end define

treePT txltree_optByPart_replacementTP (treePT optByPartTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[optByPartTP].name], "TXL_optByPart_") == 0)
        && ((! tree_plural_emptyP (optByPartTP))
            && (stringcmp (*ident_idents[tree_trees[tree_kid2TP (tree_kids[tree_trees[optByPartTP].kidsKP])].name], 
                "TXL_replacement_") == 0)));
    return (tree_kid2TP ((tree_kids[tree_trees[optByPartTP].kidsKP])));
}

bool txltree_optByPart_isAnonymous (treePT optByPartTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[optByPartTP].name], "TXL_optByPart_") == 0)
        && (! tree_plural_emptyP (optByPartTP)));
    const treePT replacementTP = txltree_optByPart_replacementTP (optByPartTP);
    assert (stringcmp (*ident_idents[tree_trees[replacementTP].name], "TXL_replacement_") == 0);
    if (tree_plural_emptyP (tree_kid1TP (replacementTP))) {
        // nothing in replacement
        return (false);
    } else {
        const treePT indExpsAndLitsTP = tree_kid1TP (tree_kid1TP (replacementTP));
        assert (stringcmp (*ident_idents[tree_trees[indExpsAndLitsTP].name], "TXL_indExpsAndLits_") == 0);
        if (!tree_plural_emptyP (tree_kid2TP (indExpsAndLitsTP))) {
            return (false);
        } else {
            const treePT expressionTP = tree_kid1TP (tree_kid1TP (indExpsAndLitsTP));
            if (stringcmp ((*ident_idents[tree_trees[expressionTP].name]), "TXL_expression_") != 0) {
                return (false);
            } else {
                return ((tree_trees[tree_kid1TP (expressionTP)].name) == anonymous_T);
            }
        }
    }
}

treePT txltree_optByPart_anonymousExpressionTP (treePT optByPartTP)
{
    assert (txltree_optByPart_isAnonymous (optByPartTP));
    const treePT replacementTP = txltree_optByPart_replacementTP (optByPartTP);
    const treePT indExpsAndLitsTP = tree_kid1TP (tree_kid1TP (replacementTP));
    return (tree_kid1TP (tree_kid1TP (indExpsAndLitsTP)));
}

// define TXL_optSkippingBracketedDescription_
//              [TXL_skippingBracketedDescription_]
//     |        [empty]
// end define
// 
// define TXL_skippingBracketedDescription_
//      'skipping [TXL_bracketedDescription_]
// end define

tokenT txltree_optSkippingNameT (treePT optSkippingTP, int n)
{
    assert (stringcmp (*ident_idents[tree_trees[optSkippingTP].name], "TXL_optSkippingBracketedDescription_") == 0);
    const treePT skippingBracketedDescriptionTP = tree_kid1TP (optSkippingTP);
    assert (stringcmp (*ident_idents[tree_trees[skippingBracketedDescriptionTP].name], "TXL_skippingBracketedDescription_") == 0);

    treePT bracketedDescriptionTP = nilTree;

    if (n == 1) {
        bracketedDescriptionTP = tree_kid2TP (skippingBracketedDescriptionTP);
        assert (stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0);
    } else if ((n == 2) && (! tree_plural_emptyP (tree_kid3TP (skippingBracketedDescriptionTP)))) {
        bracketedDescriptionTP = tree_kid1TP (tree_kid3TP (skippingBracketedDescriptionTP));
        assert (stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0);
    } else if ((n == 3) && (! tree_plural_emptyP (tree_kid4TP (skippingBracketedDescriptionTP)))) {
        bracketedDescriptionTP = tree_kid1TP (tree_kid4TP (skippingBracketedDescriptionTP));
        assert (stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0);
    }

    if (bracketedDescriptionTP == nilTree) {
        return (NOT_FOUND);
    } else {
        return (txltree_descriptionTargetT (tree_kid2TP (bracketedDescriptionTP)));
    }
}

// define TXL_argument_
//      [id] [TXL_bracketedDescription_]
// end define

tokenT txltree_formal_nameT (treePT argumentTP)
{
    assert (stringcmp (*ident_idents[tree_trees[argumentTP].name], "TXL_argument_") == 0);
    return (tree_trees[tree_kids[tree_trees[argumentTP].kidsKP]].name);
}

treePT txltree_formal_bracketedDescriptionTP (treePT argumentTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[argumentTP].name], "TXL_argument_") == 0)
         && (stringcmp (*ident_idents[tree_trees[tree_kid2TP (argumentTP)].name], "TXL_bracketedDescription_") == 0));
    return (tree_kid2TP (argumentTP));
}

tokenT txltree_formal_typeT (treePT argumentTP)
{
    assert (stringcmp (*ident_idents[tree_trees[argumentTP].name], "TXL_argument_") == 0);
    const treePT bracketedDescriptionTP = tree_kid2TP ((treePT) argumentTP);
    assert (stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0);
    const treePT descriptionTP = tree_kid2TP ((treePT) bracketedDescriptionTP);
    assert (stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0);
    return (txltree_descriptionTargetT (descriptionTP));
}

bool txltree_isQuotedLiteral (treePT literalTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_literal_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));
    const treePT k1TP = tree_kids[tree_trees[literalTP].kidsKP];
    return ((stringcmp ((*ident_idents[tree_trees[literalTP].name]), "TXL_literal_") == 0) 
         && (stringcmp ((*ident_idents[tree_trees[k1TP].name]), "TXL_quotedLiteral_") == 0));
}

tokenT txltree_literal_tokenT (treePT literalTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_literal_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));

    const treePT k1TP = tree_kids[tree_trees[literalTP].kidsKP];

    if (((tree_trees[k1TP].kind) == treeKind_order) || ((tree_trees[k1TP].kind) == treeKind_choose)) {
        if (stringcmp ((*ident_idents[tree_trees[k1TP].name]), "TXL_quotedLiteral_") == 0) {
            return (tree_trees[tree_kid1TP (tree_kid2TP (k1TP))].name);
        } else {
            assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
                 || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));
            return (tree_trees[k1TP].name);
        }
    } else {
        // direct token literal
        return (tree_trees[k1TP].name);
    }
}

tokenT txltree_literal_rawtokenT (treePT literalTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_literal_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));

    const treePT k1TP = tree_kids[tree_trees[literalTP].kidsKP];

    if (((tree_trees[k1TP].kind) == treeKind_order) || ((tree_trees[k1TP].kind) == treeKind_choose)) {
        if (stringcmp ((*ident_idents[tree_trees[k1TP].name]), "TXL_quotedLiteral_") == 0) {
            return (tree_trees[tree_kid1TP (tree_kid2TP (k1TP))].rawname);
        } else {
            assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
                 || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));
            return (tree_trees[k1TP].rawname);
        }
    } else {
        // direct token literal
        return (tree_trees[k1TP].rawname);
    }
}

enum treeKindT txltree_literal_kindT (treePT literalTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_literal_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
          || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));
    const treePT k1TP = tree_kids[tree_trees[literalTP].kidsKP];
    const enum treeKindT k1kind = tree_trees[k1TP].kind;

    if ((k1kind == treeKind_order) || (k1kind == treeKind_choose)) {
        if (stringcmp ((*ident_idents[tree_trees[k1TP].name]), "TXL_quotedLiteral_") == 0) {
            return (tree_trees[tree_kid1TP (tree_kid2TP (k1TP))].kind);
        } else {
            assert ((stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_expression_") == 0)
                 || (stringcmp (*ident_idents[tree_trees[literalTP].name], "TXL_firstTime_") == 0));
            return (k1kind);
        };
    } else {
        // direct token literal
        return (k1kind);
    }
}

tokenT txltree_ruleCall_nameT (treePT ruleCallTP)
{
    assert (stringcmp (*ident_idents[tree_trees[ruleCallTP].name], "TXL_ruleCall_") == 0);
    return (tree_trees[tree_kid2TP (ruleCallTP)].name);
}

treePT txltree_ruleCall_literalsTP (treePT ruleCallTP)
{
    assert (stringcmp (*ident_idents[tree_trees[tree_kid3TP (ruleCallTP)].name], "TXL_literals_") == 0);
    return (tree_kid3TP (ruleCallTP));
}

tokenT txltree_bracketedDescription_idT (treePT bracketedDescriptionTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0)
          && (stringcmp (*ident_idents[tree_trees[tree_kid2TP (bracketedDescriptionTP)].name], "TXL_description_") == 0));
    return (tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name);
}

treePT txltree_bracketedDescription_listRepeatOrOptTargetTP (treePT bracketedDescriptionTP)
{
    assert ((stringcmp (*ident_idents[tree_trees[bracketedDescriptionTP].name], "TXL_bracketedDescription_") == 0)
          && (stringcmp (*ident_idents[tree_trees[tree_kid2TP (bracketedDescriptionTP)].name], "TXL_description_") == 0));
    const treePT listRepeatOrOptTP = tree_kid1TP (tree_kid2TP (bracketedDescriptionTP));
    return (tree_kid2TP (listRepeatOrOptTP));
}

tokenT txltree_firstTime_nameT (treePT firstTimeTP)
{
    assert (stringcmp (*ident_idents[tree_trees[firstTimeTP].name], "TXL_firstTime_") == 0);
    return (tree_trees[tree_kids[tree_trees[firstTimeTP].kidsKP]].name);
}

tokenT txltree_firstTime_typeT (treePT firstTimeTP)
{
    assert (stringcmp (*ident_idents[tree_trees[firstTimeTP].name], "TXL_firstTime_") == 0);
    const treePT descriptionTP = tree_kid3TP (firstTimeTP);
    assert (stringcmp (*ident_idents[tree_trees[descriptionTP].name], "TXL_description_") == 0);
    return (txltree_descriptionTargetT (descriptionTP));
}

tokenT txltree_expression_baseT (treePT expressionTP)
{
    assert (stringcmp (*ident_idents[tree_trees[expressionTP].name], "TXL_expression_") == 0);
    return (tree_trees[tree_kids[tree_trees[expressionTP].kidsKP]].name);
}

treePT txltree_expression_ruleCallsTP (treePT expressionTP)
{
    assert (stringcmp (*ident_idents[tree_trees[tree_kid2TP (expressionTP)].name], "TXL_ruleCalls_") == 0);
    return (tree_kid2TP (expressionTP));
}

treePT txltree_program_statementsTP (treePT programTP)
{
    assert (stringcmp (*ident_idents[tree_trees[tree_kids[tree_trees[programTP].kidsKP]].name], "TXL_statements_") == 0);
    return (tree_kids[tree_trees[programTP].kidsKP]);
}

treePT txltree_keys_literalsTP (treePT keyListTP)
{
    assert (stringcmp (*ident_idents[tree_trees[tree_kid2TP (keyListTP)].name], "TXL_literals_") == 0);
    return (tree_kid2TP (keyListTP));
}

// define TXL_defineStatement_
//      [TXL_defineOrRedefine_] [id]
//          [TXL_optDotDotDotBar_]
//          [TXL_literalsAndBracketedDescriptions_]
//          [TXL_repBarLiteralsAndBracketedDescriptions_]
//          [TXL_optBarDotDotDot_]
//      'end [TXL_defineOrRedefine_] 
// end define

tokenT txltree_define_nameT (treePT defineTP)
{
    return (tree_trees[tree_kid2TP (defineTP)].name);
}

tokenT txltree_define_defineOrRedefineT (treePT defineTP)
{
    return (tree_trees[tree_kid1TP (tree_kid1TP (defineTP))].name);
}

tokenT txltree_define_endDefineOrRedefineT (treePT defineTP)
{
    return (tree_trees[tree_kid1TP (tree_kidTP (8, defineTP))].name);
}

treePT txltree_define_optDotDotDotBarTP (treePT defineTP)
{
    return (tree_kid3TP (defineTP));
}

treePT txltree_define_literalsAndBracketedIdsTP (treePT defineTP)
{
    return (tree_kid4TP (defineTP));
}

treePT txltree_define_barOrdersTP (treePT defineTP)
{
    return (tree_kid5TP (defineTP));
}

treePT txltree_define_optBarDotDotDotTP (treePT defineTP)
{
    return (tree_kidTP (6, defineTP));
}

// define TXL_statement_
//      [TXL_defineStatement_]          
//    | [TXL_ruleStatement_]    
//    | [TXL_functionStatement_]
// end define

treePT txltree_statement_keyDefRuleTP (treePT statementTP)
{
    return (tree_kids[tree_trees[statementTP].kidsKP]);
}

// define TXL_conditionPart_
//      [TXL_whereOrAssert_] [TXL_optNot_] [TXL_optAll_]                
//          [TXL_expression_]
// end define

bool txltree_condition_is_assert (treePT conditionTP)
{
    return ((tree_trees[tree_kid1TP (tree_kid1TP (conditionTP))].name) == assert_T);
}

treePT txltree_condition_expressionTP (treePT conditionTP)
{
    return (tree_kid4TP (conditionTP));
}

bool txltree_condition_isAnonymous (treePT conditionTP)
{
    const treePT expressionTP = tree_kid4TP ((treePT) conditionTP);
    return ((tree_trees[tree_kid1TP (expressionTP)].name) == anonymous_T);
}

bool txltree_condition_negated (treePT conditionTP)
{
    return (! tree_plural_emptyP (tree_kid2TP (conditionTP)));
}

bool txltree_condition_anded (treePT conditionTP)
{
    return (! tree_plural_emptyP (tree_kid3TP (conditionTP)));
}

bool txltree_bracketedDescriptionP (treePT treeTP)
{
    return (stringcmp ((*ident_idents[tree_trees[treeTP].name]), "TXL_bracketedDescription_") == 0);
}

bool txltree_literalP (treePT treeTP)
{
    return (stringcmp ((*ident_idents[tree_trees[treeTP].name]), "TXL_literal_") == 0);
}

bool txltree_listP (treePT bracketedDescriptionTP)
{
    return ((stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_listDescription_") == 0) 
         || (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_newlistDescription_") == 0));
}

bool txltree_repeatP (treePT bracketedDescriptionTP)
{
    return ((stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_repeatDescription_") == 0) 
         || (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_newrepeatDescription_") == 0));
}

bool txltree_list1P (treePT bracketedDescriptionTP)
{
    return ((stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]),
             "TXL_list1Description_") == 0) 
         || (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_newlist1Description_") == 0));
}

bool txltree_repeat1P (treePT bracketedDescriptionTP)
{
    return ((stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]),
             "TXL_repeat1Description_") == 0) 
         || (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_newrepeat1Description_") == 0));
}

bool txltree_optP (treePT bracketedDescriptionTP)
{
    return ((stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_optDescription_") == 0) 
         || (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
             "TXL_newoptDescription_") == 0));
}

bool txltree_attrP (treePT bracketedDescriptionTP)
{
    return (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
            "TXL_attrDescription_") == 0);
}

bool txltree_seeP (treePT bracketedDescriptionTP)
{
    return (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
            "TXL_seeDescription_") == 0);
}

bool txltree_notP (treePT bracketedDescriptionTP)
{
    return (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]),
            "TXL_notDescription_") == 0);
}

bool txltree_fenceP (treePT bracketedDescriptionTP)
{
    return (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
            "TXL_fenceDescription_") == 0);
}

bool txltree_pushP (treePT bracketedDescriptionTP)
{
    return (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]),
            "TXL_pushDescription_") == 0);
}

bool txltree_popP (treePT bracketedDescriptionTP)
{
    return (stringcmp ((*ident_idents[tree_trees[tree_kid1TP (tree_kid2TP (bracketedDescriptionTP))].name]), 
            "TXL_popDescription_") == 0);
}
